--
-- FactionDiscount_AutoStock
-- Author: Nafe
-- Guild: Clan Blackhoof
-- Server: Cenarion Circle
--

local _G = getfenv(0)

local FD_AS = {}
_G.FactionDiscount_AutoStock = FD_AS

--stack references for globals
local GetRealmName = _G.GetRealmName
local UnitName = _G.UnitName

local UnitReaction = _G.UnitReaction

local GetMerchantNumItems = _G.GetMerchantNumItems
local GetMerchantItemInfo = _G.GetMerchantItemInfo
local GetMerchantItemLink = _G.GetMerchantItemLink
local GetMerchantItemMaxStack = _G.GetMerchantItemMaxStack

--TODO: stack reference: MerchantNextPageButton:Click()
--TODO: stack reference: MerchantPrevPageButton:Click()

local table_insert = _G.table.insert

local GetMoney = _G.GetMoney
local BuyMerchantItem = _G.BuyMerchantItem

local tonumber = _G.tonumber

local NUM_BAG_SLOTS = _G.NUM_BAG_SLOTS
local GetContainerNumFreeSlots = _G.GetContainerNumFreeSlots
local GetItemCount = _G.GetItemCount
local GetItemFamily = _G.GetItemFamily
local GetItemInfo = _G.GetItemInfo

local GetNumGlyphSockets = _G.GetNumGlyphSockets
local GetGlyphSocketInfo = _G.GetGlyphSocketInfo
local GetNumTalentGroups = _G.GetNumTalentGroups
local UnitClass = _G.UnitClass
local UnitLevel = _G.UnitLevel

local strlower = _G.strlower

--local strsplit = _G.strsplit
--local select = _G.select
--local strfind = _G.string.find
local strgsub = _G.string.gsub
local strgmatch = _G.string.gmatch
local strmatch = _G.string.match

local floor = _G.floor
local ceil = _G.ceil
local modulus = _G.math.fmod

local time = _G.time

----Saved variables----
--Set at VARIABLES_LOADED event
local FD_AS_saved = {}


----Local variables----
local txt2threshold = {
   ["always"]     = 4,
   ["honored"]    = 6,
   ["revered"]    = 7,
   ["exalted"]    = 8,
}
local threshold2txt = {
   [4] = "Always",
   [6] = "Honored",
   [7] = "Revered",
   [8] = "Exalted",
}
local discount2txt = {
   [4] = "Neutral",
   [5] = "Friendly or Unfactioned",
   [6] = "Honored",
   [7] = "Revered",
   [8] = "Exalted",
}
local default_threshold = 8

local default_throttle = 5
local last_interact_time = 0

local default_class_stocks = {
   ["level"] = 80,
   ["MAGE"] = {
      [17020] = 0,
      [17031] = 0,
      [17032] = 0,
   },
   ["ROGUE"] = {
      [3775] = 0,
      [5237] = 0,
      [43231] = 0,
      [43233] = 0,
      [43235] = 0,
      [43237] = 0,
   },
   ["DEATHKNIGHT"] = {
      [37201] = 60200,
   },
   ["PRIEST"] = {
      [44615] = 0,
   },
   ["DRUID"] = {
      [44605] = 0,
      [44614] = 57857,
   },
   ["SHAMAN"] = {
      [17030] = 58059,  --TODO: this needs to be tested: Shaman Ankh Glyph
   },
   ["PALADIN"] = {
      [17033] = 0,
      [21177] = 0,
   },
   ["WARLOCK"] = {
      [5565] = 0,
      [16583] = 0,
   },
}

local slash_subcommands_info = {
   {
      ["name"] = "",
      ["desc"] = "This print-out!",
   },
   {
      ["name"] = "|cff00ff00enable|r",
      ["desc"] = "Enables autobuying from merchants for this character.",
   },
   {
      ["name"] = "|cffff0000disable|r",
      ["desc"] = "Disables autobuying from merchants for this character.",
   },
   {
      ["name"] = "|cff0000ffstatus|r",
      ["desc"] = "Displays enable state and faction discount threshold for this character.",
   },
   {
      ["name"] = "|cff00ffffclass|r",
      ["desc"] = "Sets the default autobuy preferences (1 full stack per item) for your class.  |cffff0000Only useable at level "..default_class_stocks.level.."|r."
   },
   {
      ["name"] = "|cffff00fflist|r",
      ["desc"] = "Displays all items and quantities thereof to buy for this character.",
   },
   {
      ["name"] = "|cffff0000clear|r",
      ["desc"] = "Removes all autobuy preferences for this character.",
   },
   {
      ["name"] = "|cffffff00always | honored | revered | exalted|r",
      ["desc"] = "Sets the faction discount threshold for this character.",
   },
   {
      ["name"] = "# |cffe5cc80[ItemLink]|r",
      ["desc"] = "Sets the autobuy preferences for the item, with given quantity # (0 = don't buy).  More than 1 quantity/link can be entered, and the order doesn't matter.  First quantity is matched to the first link and so on.",
   },
}

--Frame for OnUpdate
local OUframe = CreateFrame("Frame", "FDAS_OU_FRAME")
OUframe.elapsed = 0

local scanTimeInterval = 0.5
local maxScanAttempts = 10

--Set at VARIABLES_LOADED event
local realm = nil
local pName = nil

-- Loading Function --
function FD_AS.OnLoad(self)
   local LOAD_MSG = "Loaded."
   SLASH_FD_AS1="/FDAS"
   SlashCmdList["FD_AS"] = FD_AS.SlashHandler
   FD_AS.SetUpSlashCommands()
   
   FD_AS.HeaderMsg(LOAD_MSG)
   
   self:RegisterEvent("VARIABLES_LOADED")
   
   FD_AS.frame = self
end

-----------------------------------------------------------
-------------------- Slash Handlers =) --------------------
-----------------------------------------------------------
local FD_AS_slash = {}
_G.FD_AS_slash = FD_AS_slash

function FD_AS.SlashHandler(parm)
   if (parm == nil or parm == "") then
      FD_AS.SlashHandler_Default()
   else
      parm = FD_AS.trim(parm)
      local smallParm = strlower(parm)
      
      local slashHandler = FD_AS_slash[smallParm]
      
      if (slashHandler) then
         slashHandler(smallParm)
      else
         --here we use the setItem slash handler
         
         local success = FD_AS.SlashHandler_setItem(parm)
         
         if not success then
            FD_AS.HeaderMsg("Unrecognized Slash Command or incorrect syntax.")
         end
      end
   end
end

function FD_AS.SetUpSlashCommands()
   for key,value in pairs(txt2threshold) do
      FD_AS_slash[key] = FD_AS.SlashHandler_threshold
   end
end

function FD_AS.SlashHandler_Default()
   --list available slash commands
   
   FD_AS.HeaderMsg("Available slash commands:")
   local slash = strlower(SLASH_FD_AS1)
   
   for _,entry in ipairs(slash_subcommands_info) do
      FD_AS.LocalMsg("  ".. slash.." "..entry.name.."   -   "..entry.desc)
   end
end
function FD_AS.SlashHandler_threshold(threshold)
   FD_AS.SetThreshold(txt2threshold[threshold])
   local thresholdText = FD_AS.GetThresholdText()
   FD_AS.HeaderMsg("Threshold now set to |cff00ff00"..thresholdText.."|r.")
end
function FD_AS.SlashHandler_setItem(parm)
   --attempt to extract tables for itemIds and quantities
   local didSomething = false
   
   local itemIds = {}
   local quantities = {}
   
   
   --extract itemIds for each itemLink in sequence
   for s_itemId in strgmatch(parm, "|c%x+|Hitem:(%d+):[:%d]+|h%[[^%[%]]+%]|h|r") do
      table_insert(itemIds, tonumber(s_itemId))
   end
   
   --remove all itemLinks from the string and replace them with a space
   parm = strgsub(parm, "|c%x+|Hitem[:%d]+|h%[[^%[%]]+%]|h|r", " ")
   
   --now extract all integer quantities
   for s_quantity in strgmatch(parm, "%d+") do
      table_insert(quantities, tonumber(s_quantity))
   end
   
   if (#itemIds == #quantities) then
      for i=1,#itemIds do
         FD_AS.SetItemStockPreference(itemIds[i], quantities[i])
         FD_AS.AnnounceChangedPreference(itemIds[i], quantities[i])
      end
      didSomething = true
   else
      FD_AS.HeaderMsg("The number of itemLinks ("..#itemIds..") and number of quantities ("..#quantities..") do not match!")
   end

   ----old version
   ----I wish I didn't suck at RegEx =\
   --for s_quantity, s_itemId in strgmatch(parm, "(%d+)%s-|c%x+|Hitem:(%d+):[:%d]+|h%[[^%[%]]+%]|h|r") do
   --   local quantity = tonumber(s_quantity)
   --   local itemId = tonumber(s_itemId)
   --   
   --   FD_AS.SetItemStockPreference(itemId, quantity)
   --   FD_AS.AnnounceChangedPreference(itemId, quantity)
   --   
   --   didSomething = true
   --end
   
   return didSomething
end

function FD_AS_slash.enable(parm)
   FD_AS.SetEnabled()
   FD_AS.HeaderMsg("Enabled.")
end
function FD_AS_slash.disable(parm)
   FD_AS.SetDisabled()
   FD_AS.HeaderMsg("Disabled.")
end
function FD_AS_slash.status(parm)
   FD_AS.PrintStatus()
end
function FD_AS_slash.class(parm)
   FD_AS.SetClassDefaults()
end
function FD_AS_slash.list(parm)
   FD_AS.ListCurrentPreferences()
end
function FD_AS_slash.clear(parm)
   FD_AS.ClearItemStockPreferences()
end
-----------------------------------------------------------
-------------------- Event Handlers =) --------------------
-----------------------------------------------------------
local FD_AS_event = {}

function FD_AS_event.VARIABLES_LOADED(...)
   --get saved variable table to local table
   if(_G.FactionDiscount_AutoStock_saved) then
      FD_AS_saved = _G.FactionDiscount_AutoStock_saved
   else
      _G.FactionDiscount_AutoStock_saved = FD_AS_saved
   end
   
   realm = GetRealmName()
   pName = UnitName("player")
   
   FD_AS.ConstructSaveFile()
   
   --this looks weird, but what it's doing is checking if we *should* be enabled, then actually enabling if so
   if( FD_AS.IsEnabled() and FD_AS.PlayerStocksItems() ) then
      FD_AS.Enable()
   end
end

function FD_AS_event.MERCHANT_SHOW(...)
   if( FD_AS.CheckNPC() ) then
      --throttle the event so we don't buy too much
      if( FD_AS.AutobuyReady() ) then
         FD_AS.CancelRescanQueue()
         FD_AS.ScanAndBuy(0)
      end
   end
end
function FD_AS.OnEvent(event, ...)
   if (FD_AS_event[event]) then
      FD_AS_event[event](...)
      return
   end
   
   FD_AS.HeaderMsg("Unhandled Event: "..event)
end --FD_AS.OnEvent

-----------------------------------------------------------
------------- Functions for Handling Savefile -------------
-----------------------------------------------------------
function FD_AS.ConstructSaveFile()
   --build data structure
   if not FD_AS_saved then
      FD_AS_saved = {}
   end
   if not FD_AS_saved[realm] then
      FD_AS_saved[realm] = {}
   end
   if not FD_AS_saved[realm][pName] then
      FD_AS_saved[realm][pName] = {}
      FD_AS_saved[realm][pName].enabled = true
      FD_AS_saved[realm][pName].threshold = default_threshold
   end
   if not FD_AS_saved[realm][pName].items then
      FD_AS_saved[realm][pName].items = {}
   end
end

--Does the player stock up on any items?
function FD_AS.PlayerStocksItems()
   for k,v in pairs (FD_AS_saved[realm][pName].items) do
      return true
   end
   
   return false
end
--Does the player stock up on this item?  This function may not be needed, we can likely use BlahBlahItemCount
function FD_AS.GetStocksItemBool(itemId)
   --just to make sure we are using numbers
   itemId = tonumber(itemId)
   
   if (FD_AS_saved[realm][pName].items[itemId]) then
      return true
   else
      return false
   end
end
--How many of this item does the player prefer to stock?
function FD_AS.GetItemStockPreferenceCount(itemId)
   --just to make sure we are using numbers
   itemId = tonumber(itemId)
   
   if (FD_AS_saved[realm][pName].items[itemId]) then
      return FD_AS_saved[realm][pName].items[itemId]
   else
      return 0
   end
end
--Sets the player's item stock preference
function FD_AS.SetItemStockPreference(itemId, quantity)
   --just to make sure we are using numbers
   itemId = tonumber(itemId)
   quantity = tonumber(quantity)
   
   if (quantity > 0) then
      FD_AS_saved[realm][pName].items[itemId] = quantity
      
      --make sure we are registered for the event
      if( FD_AS.IsEnabled() ) then
         FD_AS.Enable()
      end
   else
      FD_AS.RemoveItemStockPreference(itemId)
      
      --make sure we de-register the event
      if( not FD_AS.PlayerStocksItems() ) then
         FD_AS.Disable()
      end
   end
end
--Removes the item from the player's stock preferences
function FD_AS.RemoveItemStockPreference(itemId)
   --just to make sure we are using numbers
   itemId = tonumber(itemId)
   
   FD_AS_saved[realm][pName].items[itemId] = nil
end

function FD_AS.ClearItemStockPreferences()
   for itemId, quantity in pairs(FD_AS_saved[realm][pName].items) do
      FD_AS.SetItemStockPreference(itemId, 0)
      FD_AS.AnnounceChangedPreference(itemId, 0)
   end
end

------------------------------------------------------------
------------ Functions for Handling Addon state ------------
------------------------------------------------------------
function FD_AS.IsEnabled()
   return FD_AS_saved[realm][pName].enabled
end

function FD_AS.Enable()
   FD_AS.frame:RegisterEvent("MERCHANT_SHOW")
end
function FD_AS.Disable()
   FD_AS.frame:UnregisterEvent("MERCHANT_SHOW")
end

function FD_AS.SetEnabled()
   FD_AS_saved[realm][pName].enabled = true
   FD_AS.Enable()
end
function FD_AS.SetDisabled()
   FD_AS_saved[realm][pName].enabled = false
   FD_AS.Disable()
end

------------------------------------------------------------
------------- Functions for Handling Threshold -------------
------------------------------------------------------------
function FD_AS.GetThreshold()
   return FD_AS_saved[realm][pName].threshold
end
function FD_AS.SetThreshold(newThreshold)
   FD_AS_saved[realm][pName].threshold = newThreshold
end

function FD_AS.GetThresholdText()
   return threshold2txt[FD_AS.GetThreshold()]
end

-------------------------------------------------------------
------------- Functions for Handling NPC check  -------------
-------------------------------------------------------------
function FD_AS.GetNPCdiscount()
   local discount = UnitReaction("npc", "player")
   return discount
end
function FD_AS.GetNPCdiscountText()
   return discount2txt[FD_AS.GetNPCdiscount()]
end

function FD_AS.CheckNPC()
   --ensure NPC meets threshold
   local discount = FD_AS.GetNPCdiscount()
   local threshold = FD_AS.GetThreshold()
   
   if ( discount >= threshold ) then return true end
   
   return false
end

------------------------------------------------------------
-------- Functions for Handling merchant purchasing --------
------------------------------------------------------------

--Called if the merchant meets cetain discount
--Here we see if there's anything we want to buy, and buy it if so
function FD_AS.ScanAndBuy(prevAttempts)
   local numItems = GetMerchantNumItems()
   
   --check to see if all of the merchant's items are in the cache
   local allInCache, merchantItemIds = FD_AS.CheckMerchantItemIds(numItems)
   
   if (allInCache == true) then
      local boughtSomething = false
      
      for i=1,numItems do
         local merchantItemId = merchantItemIds[i]
         
         --buy if we match item, and record that we bought something
         local boughtThisTime = FD_AS.CheckAndBuy(merchantItemId, i)
         boughtSomething = boughtSomething or boughtThisTime
      end
      
      --announce and throttle so we don't buy anything before the inventory registers new items
      if (boughtSomething) then
         FD_AS.AnnounceBought()
         FD_AS.ThrottleAutobuy()
      end
   elseif (prevAttempts == 0) then
      --we don't have the info cached yet. Let's load it
      
      --paginate the merchant's window in order to load all items to the cache
      FD_AS.PaginateMerchant(numItems)
      
      --let's rescan
      FD_AS.QueueRescanMerchant(prevAttempts)
   elseif (prevAttempts < maxScanAttempts) then
      --data hasn't returned from the server yet'
      
      --let's rescan (again =\)
      FD_AS.QueueRescanMerchant(prevAttempts)
   else
      --we've tried scanning for a while now.  Alert the user and give up
      FD_AS.HeaderMsg("It appears you are experiencing latency issues and FDAS was unable to scan the merchant.")
   end
end
function FD_AS.CheckMerchantItemIds(numItems)
   local merchantItemIds = {}
   
   for i=1,numItems do
      local merchantLink = GetMerchantItemLink(i)
      
      if (not merchantLink) then
         --data isn't loaded in the cache
         return false, {}
      end
      
      table_insert(merchantItemIds, FD_AS.GetItemIdFromLink(merchantLink))
   end
   
   return true, merchantItemIds
end
function FD_AS.PaginateMerchant(numItems)
   if (numItems > 10) then
      local movePages = ceil(numItems/10)
      for i=1,movePages do
         MerchantNextPageButton:Click()
      end
      
      --return to page 1 here
      for i=1,movePages do
         MerchantPrevPageButton:Click()
      end
   end
end
function FD_AS.QueueRescanMerchant(prevAttempts)
   --set elapsed time to 0
   OUframe.elapsed = 0
   
   --enable OnUpdate script
   OUframe:SetScript("OnUpdate", function(...) FD_AS.HandleOnUpdate(prevAttempts+1, ...) end)
end
function FD_AS.CancelRescanQueue()
   --disable OnUpdate script
   OUframe:SetScript("OnUpdate", nil)
end
function FD_AS.HandleOnUpdate(setAttempt, self, elapsed)
   OUframe.elapsed = OUframe.elapsed+elapsed
   
   if (OUframe.elapsed >= scanTimeInterval) then
      FD_AS.CancelRescanQueue()
      
      --check if we're still at the NPC!
      if (UnitName("npc") ~= nil) then
         FD_AS.ScanAndBuy(setAttempt)
      end
   end
end

--Here we see if the player stocks the individual item, and buy it if we need to
function FD_AS.CheckAndBuy(itemId, merchantIndex)
   
   --if we bought something, return true, else false
   if( FD_AS.GetStocksItemBool(itemId) ) then
      return FD_AS.BuyItemIfAble(itemId, merchantIndex)
   end
   
   return false
end
function FD_AS.BuyItemIfAble(itemId, merchantIndex)
   --if we bought something, return true, else false
   local _,_,itemCost, batchCount, numAvailable = GetMerchantItemInfo(merchantIndex)
   
   local pMoney = GetMoney()
   
   --not enough money
   if( pMoney < itemCost ) then return false end
   
   local batchesWanted = floor(FD_AS.PlayerWantsItemCount(itemId)/batchCount)
   
   --we're fully stocked or there's not enough space
   if batchesWanted == 0 then return false end
   
   --non-limited seller, let's ignore the presence of this
   if (numAvailable == -1) then numAvailable = batchesWanted end
   
   local countToBuy = FD_AS.least(batchesWanted, numAvailable, floor(pMoney/itemCost))
   
   local _,_,_,_,_,_,_,itemStackSize = GetItemInfo(itemId)
   local modifiedStackSize = itemStackSize/batchCount
   while (countToBuy > modifiedStackSize) do
      --buy extra stacks
      BuyMerchantItem(merchantIndex, modifiedStackSize)
      countToBuy = countToBuy - modifiedStackSize
   end
   
   BuyMerchantItem(merchantIndex, countToBuy)
   return true
end
--How many of this item does the player want to purchase?  This becomes reduced if the player can't hold the full amount
function FD_AS.PlayerWantsItemCount(itemId)
   --get number of items player wishes to stock
   local stockTotal = FD_AS.GetItemStockPreferenceCount(itemId)
   local currentStock = GetItemCount(itemId)
   
   --then subtract current number of items
   local playerWants = stockTotal - currentStock
   
   --we don't want to be negative here
   if (playerWants < 0) then playerWants = 0 end
   
   --we are safe up to one stack w/o having to check if there's extra inventory space
   local _,_,_,_,_,_,_,itemStackSize = GetItemInfo(itemId)
   
   --if the player needs to buy multiple stacks beyond what we have, we must check if there's space:
   if (playerWants > itemStackSize) then
      --check if there is inventory space if we need to buy more than filling up the current stack
      
      local invSlotsNeeded = ceil(playerWants/itemStackSize)
      
      --if there is no extra space, reduce the count as needed
      playerWants = modulus(playerWants,itemStackSize) + itemStackSize*FD_AS.InventorySlotsAvailable(itemId)
   end
   
   return playerWants
end
--This checks how many inventory slots are open for the player to buy for the specified item
--Currently imperfect if the player doesn't keep full stacks of the item
----I just didn't want to account for that because it requires scanning the entire inventory =\
function FD_AS.InventorySlotsAvailable(itemId)
   --TODO: consider bag types
   local slotsAvailable = 0
   for i=0, NUM_BAG_SLOTS do
      local numFree, bagType = GetContainerNumFreeSlots(i)
      
      --TODO: use itemFamily here
      if (bagType == 0 ) then
         slotsAvailable = slotsAvailable + numFree
      end
   end
   return slotsAvailable
end

function FD_AS.AutobuyReady()
   if( last_interact_time + default_throttle <= time() ) then
      return true
   else
      return false
   end
end
function FD_AS.ThrottleAutobuy()
   last_interact_time = time()
end

-------------------------------------------------------------
----------- Functions for handling class defaults -----------
-------------------------------------------------------------
function FD_AS.CheckForGlyph(checkSpellId)
   for spec=1,GetNumTalentGroups() do
      for i=1,GetNumGlyphSockets(spec) do
         local enabled, _, glyphSpellId = GetGlyphSocketInfo(i, spec)
         
         if (enabled) then
            if (glyphSpellId == checkSpellId) then
               return true
            end
         end
      end
   end
   
   return false
end

--TODO: update FD_AS.SetClassDefaults()
function FD_AS.SetClassDefaults()
   local _,pClass = UnitClass("player")
   local pLevel = UnitLevel("player")
   
   if ( pLevel == default_class_stocks.level ) then
      local thisClassDefaults = default_class_stocks[pClass]
      if (thisClassDefaults) then
         local allReagentsCoveredByGlyphs = true
         for itemId,glyphId in pairs(thisClassDefaults) do
            if (glyphId == 0 or not FD_AS.CheckForGlyph(glyphId)) then
               allReagentsCoveredByGlyphs = false
               local _,_,_,_,_,_,_,itemStackSize = GetItemInfo(itemId)
               if (itemStackSize) then
                  --automatically set to 1 stack's worth of the item
                  FD_AS.SetItemStockPreference(itemId, itemStackSize)
                  FD_AS.AnnounceChangedPreference(itemId, itemStackSize)
               else
                  --appears the function didn't return, indicating we can't access the data. Cache problem?
                  FD_AS.HeaderMsg("It appears you do not have a default item ("..itemId..") loaded in your cache.  This is a very rare occurance, likely occuring only on patch day or soon after.  Until you see the item, this function will not perform properly.")
               end
            end
         end
         --TODO: test allReagentsCoveredByGlyphs
         if (allReagentsCoveredByGlyphs) then
            FD_AS.HeaderMsg("All class defaults are covered by glyphs in your specs that remove the reagent cost.")
         end
      else
         FD_AS.HeaderMsg("Your class does not have any defaults.")
      end
   else
      FD_AS.HeaderMsg("This feature is unfortunately only available at level "..default_class_stocks.level..".")
   end
end

------------------------------------------------------------
--- Functions for general handling, not directly related ---
------------------------------------------------------------
function FD_AS.GetItemIdFromLink(itemLink)
   --this can probably be improved, but I suck at RegEx =\
   --return tonumber(select( strsplit(":", select( strfind(itemLink, "^|c%x+|H(.+)|h%[.*%]"), 3)), 2))
   
   --here's my attempt to improve it:
   --|cffffffff|Hitem:17020:0:0:0:0:0:0:0:0|h[Arcane Powder]|h|r
   return tonumber(strmatch(itemLink, "^|c%x+|Hitem:(%d+):.+|h%[[^%[%]]+%]|h|r$"))
end

--TODO: can I make it this?: local function FD_AS.least(a, b, ...)
function FD_AS.least(a, b, ...)
   --this probably exists in LUA, but I don't know it, so here's a simple recursive function
   local lesser = a
   if (b < a) then lesser = b end
   
   
   local argNums = select('#', ...)
   if (argNums > 0) then
      return FD_AS.least(lesser, ...)
   end
   return lesser
end

function FD_AS.trim(str)
   return (strgsub(str, "^%s*(.-)%s*$", "%1"))
end

-----------------------------------------------------------
------ Functions for showing output on client screen ------
-----------------------------------------------------------
function FD_AS.PrintStatus()
   local enabledText
   if( FD_AS.IsEnabled() ) then
      enabledText = "Enabled"
   else
      enabledText = "Disabled"
   end
   local thresholdText = FD_AS.GetThresholdText()
   
   local announceString = "FDAS is |cff00ff00"..enabledText.."|r and has a faction threshold of: |cff00ff00"..thresholdText.."|r."
   
   FD_AS.HeaderMsg(announceString)
end

function FD_AS.AnnounceChangedPreference(itemId, quantity)
   local _,itemLink = GetItemInfo(itemId)
   local announceString = nil
   if (quantity <= 0) then
      announceString = "Stock preference for "..itemLink.." removed."
   else
      announceString = "Stock preference for "..itemLink.." set to "..quantity.."."
   end
   
   FD_AS.HeaderMsg(announceString)
end

function FD_AS.AnnounceBought()
   local NPCname = UnitName("npc")
   local discountText = FD_AS.GetNPCdiscountText()
   local announceString = "Bought items at |cff00ff00"..NPCname.."|r with a |cff00ff00"..discountText.."|r discount."
   
   FD_AS.HeaderMsg(announceString)
   
   --TODO: make more informative in case of error
end

function FD_AS.ListCurrentPreferences()
   FD_AS.HeaderMsg("Your current preferences:")
   local listed = false
   for itemId, quantity in pairs(FD_AS_saved[realm][pName].items) do
      local _, itemLink = GetItemInfo(itemId)
      if (itemLink) then
         FD_AS.LocalMsg(quantity .." ".. itemLink)
      else
         --appears the function didn't return, indicating we can't access the data. Cache problem?
         FD_AS.HeaderMsg("It appears you do not have data for an item ("..itemId..") loaded in your cache.  This is a very rare occurance, likely occuring only on patch day or soon after.  Until you see the item, this function will not perform properly.")
      end
      
      listed = true
   end
   
   if (not listed) then
      FD_AS.LocalMsg("No preferences found!")
   end
end

--the guts
function FD_AS.HeaderMsg(txt)
   FD_AS.LocalMsg("[FactionDiscount |cff00ff00AutoStock|r] "..txt)
end
function FD_AS.LocalMsg(txt)
   DEFAULT_CHAT_FRAME:AddMessage(txt)
end

